package net.melon.logger.manage;

import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import net.esj.basic.core.engine.util.logging.Logger.LEVEL;
import net.esj.basic.core.engine.util.logging.LoggerFactory;
import net.esj.basic.dao.jdbc.JdbcDao;
import net.esj.basic.exception.MelonRuntimeException;
import net.esj.basic.pojo.op.Operator;
import net.esj.basic.utils.DateUtils;
import net.esj.basic.utils.UUIDCreator;
import net.esj.basic.utils.Validators;
import net.melon.jdbc.core.RowMapper;
import net.melon.logger.NoSuchLoggerMethodException;
import net.melon.logger.pojo.Logger;
import net.melon.logger.pojo.LoggerMethod;
import net.melon.logger.pojo.LoggerMethod.OutputType;
import net.melon.logger.tail.LogFileTailerThread;
import net.melon.logger.tail.PassiveLogTail;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.RollingFileAppender;

import com.opensymphony.xwork2.util.ValueStack;

@Singleton
@Named("loggerManage")
public class LoggerManage {
	
	private LogFileTailerThread thread;
	
	private PassiveLogTail passiveLogTailListener =  new PassiveLogTail();
	
	private JdbcDao jdbcDao;
	
	List<LoggerMethod> cache = new ArrayList<LoggerMethod>();
	
	public LoggerMethod findMethod(String className,String methodName,String resultName){
		if(cache.isEmpty()){
			reloadMethods();
		}
		for(LoggerMethod lm : cache){
			if(StringUtils.equals(lm.getActionName(), className) && 
					StringUtils.equals(lm.getMethodName(),methodName) &&
					(StringUtils.equals(lm.getResultName(), resultName)||Validators.isEmpty(resultName))){
				return lm;
			}
		}
		throw new NoSuchLoggerMethodException("no such logger method");
	}
	
	private static final String RELOAD_METHODS = "SELECT * FROM CORE_LOGGER_METHOD ";
	
	public void reloadMethods(){
		cache.clear();
		
		jdbcDao.getJdbcTemplate().query(RELOAD_METHODS, new RowMapper<LoggerMethod>() {

			@Override
			public LoggerMethod mapRow(ResultSet set, int arg1)
					throws SQLException {
				LoggerMethod method = new LoggerMethod();
				method.setArguments(set.getString("ARGUMENTS"));
				method.setActionName(set.getString("ACTION_NAME"));
				method.setResultName(set.getString("RESULT_NAME"));
				method.setFile(set.getString("FILE"));
				method.setId(set.getString("ID"));
				method.setLevel(set.getString("LEVEL"));
				method.setMessage(set.getString("MESSAGE"));
				method.setMethodName(set.getString("METHOD_NAME"));
				method.setOutputType(set.getString("OUTPUT_TYPE"));
				cache.add(method);
				return method;
			}
		});
	}
	
	public String resolveLog(LoggerMethod method,ValueStack stack){
		String msg = method.getMessage();
		if(net.esj.basic.utils.StringUtils.hasText(method.getArguments())){
			String[] args = method.getArguments().split(",");
			for(String arg : args){
				String val = stack.findString(arg);
				msg = msg.replaceFirst("\\?", val);
			}
		}
		return msg;
	}
	
	public Logger logger(LoggerMethod method,ValueStack stack,Operator operator){
		Logger logger = new Logger();
		logger.setGmtCreate(DateUtils.now());
		logger.setMessage(resolveLog(method, stack));
		logger.setMethodId(method.getId());
		if(operator!=null){
			logger.setOperatorId(operator.getId().toString());
			logger.setOperatorName(operator.getName());
			logger.setOperatorType(operator.getMType());
		}
		
		if(StringUtils.equalsIgnoreCase(method.getOutputType(), OutputType.DATABASE.name())){
			saveLoggerToDB(logger);
		}else if(StringUtils.equalsIgnoreCase(method.getOutputType(), OutputType.FILE.name())){
			RollingFileAppender appender = getAppender(method.getFile());
			
			net.esj.basic.core.engine.util.logging.Logger log = LoggerFactory.getLogger(method.getActionName());
			log.logger(method.getMethodName(), logger.toString(), LEVEL.valueOf(method.getLevel()));
		}
		return logger;
	}
	
	public void startTail(String filePath) throws MelonRuntimeException{
		File file = new File(filePath);
		if(!file.exists()){
			throw new MelonRuntimeException("该文件不存在");
		}
		if(thread!=null){
			thread.stopTailing();
			thread = null;
		}
		thread = new LogFileTailerThread(file);
		thread.addLogFileTailerListener(passiveLogTailListener);
		thread.start();
	}
	
	public Iterator<String> tail(){ 
		return passiveLogTailListener.get();
	}
	
	public void stopTail(){
		if(thread!=null){
			thread.stopTailing();
			thread = null;
		}
	}
	
	private Map<String,RollingFileAppender> appenderCache = new HashMap<String,RollingFileAppender>();
	
	private RollingFileAppender getAppender(String file){
		RollingFileAppender appender = appenderCache.get(file);
		if(appender==null){
			try {
				Layout layout = new PatternLayout();
				appender = new RollingFileAppender(layout, file);
				appender.setMaxFileSize("1024KB");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return appender;
	}
	
	private static final String INSERT_LOGGER = "INSERT INTO CORE_LOGGER(ID,OPERATOR_ID,OPERATOR_NAME,OPERATOR_TYPE,MESSAGE,METHOD_ID,GMT_CREATE) VALUES (?,?,?,?,?,?,?)";
	public void saveLoggerToDB(Logger logger){
		logger.setId(UUIDCreator.create());
		jdbcDao.getJdbcTemplate().update(INSERT_LOGGER, new Object[]{
				logger.getId(),logger.getOperatorId(),logger.getOperatorName(),logger.getOperatorType(),logger.getMessage(),logger.getMethodId(),logger.getGmtCreate()
		}, new int[]{Types.CHAR,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,Types.LONGVARCHAR,Types.VARCHAR,Types.TIMESTAMP});
	}

	protected void saveLoggerToFile(Logger logger,LoggerMethod method){
		
	}
	
	@Inject
	public void setJdbcDao(JdbcDao jdbcDao) {
		this.jdbcDao = jdbcDao;
	}
	
	
}
